/*-------------------------------------------------------------------------
 *
 * lmgr.c
 *	  POSTGRES 锁管理器代码
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/storage/lmgr/lmgr.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/subtrans.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "commands/progress.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "storage/sinvaladt.h"
#include "utils/inval.h"


/*
 * 每个后端计数器，用于生成投机插入令牌。
 *
 * 这可能会绕回，但这没关系，因为它仅在插入元组和检查
 * 没有（唯一）约束违规之间的短时间内使用。理论上，后端
 * 可能会看到由另一个后端投机插入的元组，但在它开始等待
 * 令牌之前，另一个后端完成了插入，并且执行了2^32个
 * 无关的插入。在这一切之后，第一个后端最终调用
 * SpeculativeInsertionLockAcquire()，意图等待第一个插入完成，
 * 但最终却在等待最新的无关插入。即便如此，也不会发生
 * 特别严重的事情：在最坏的情况下，它们会死锁，导致其中
 * 一个事务中止。
 */
static uint32 speculativeInsertionToken = 0;


/*
 * 结构以保存事务锁等待的上下文信息。
 *
 * 'oper'是需要等待其他事务的操作；'rel'和'ctid'指定
 * 被等待的元组的地址。
 */
typedef struct XactLockTableWaitInfo
{
	XLTW_Oper	oper;
	Relation	rel;
	ItemPointer ctid;
} XactLockTableWaitInfo;

static void fc_XactLockTableWaitErrorCb(void *fc_arg);

/*
 * RelationInitLockInfo
 *		初始化关系描述符中的锁信息。
 *
 *		relcache.c必须在创建任何reldesc时调用此函数。
 */
void RelationInitLockInfo(Relation fc_relation)
{
	Assert(RelationIsValid(fc_relation));
	Assert(OidIsValid(RelationGetRelid(fc_relation)));

	fc_relation->rd_lockInfo.lockRelId.relId = RelationGetRelid(fc_relation);

	if (fc_relation->rd_rel->relisshared)
		fc_relation->rd_lockInfo.lockRelId.dbId = InvalidOid;
	else
		fc_relation->rd_lockInfo.lockRelId.dbId = MyDatabaseId;
}

/*
 * SetLocktagRelationOid
 *		根据关系OID设置关系的锁标记
 */
static inline void fc_SetLocktagRelationOid(LOCKTAG *fc_tag, Oid fc_relid)
{
	Oid			fc_dbid;

	if (IsSharedRelation(fc_relid))
		fc_dbid = InvalidOid;
	else
		fc_dbid = MyDatabaseId;

	SET_LOCKTAG_RELATION(*fc_tag, fc_dbid, fc_relid);
}

/*
 *		LockRelationOid
 *
 * 根据OID锁定关系。一般情况下在尝试打开
 * 关系的relcache条目之前使用。
 */
void LockRelationOid(Oid fc_relid, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;
	LOCALLOCK  *fc_locallock;
	LockAcquireResult fc_res;

	fc_SetLocktagRelationOid(&fc_tag, fc_relid);

	fc_res = LockAcquireExtended(&fc_tag, fc_lockmode, false, false, true, &fc_locallock);

	/*
	 * 现在我们已经获得了锁，检查失效消息，以便在
	 * 我们尝试使用之前更新或刷新任何过时的relcache条目。
	 * RangeVarGetRelid()特别依赖于我们来做到这一点。如果
	 * 我们已经有了相同类型的锁请求，则可以跳过这一步，
	 * 因为此时没有其他人能够以不良方式修改
	 * relcache条目。（在我们自己的事务修改
	 * 关系的情况下，relcache的更新通过
	 * CommandCounterIncrement发生，而不是在这里。）
	 *
	 * 然而，在代码递归作用于表（通常是目录）的
	 * 角落情况下，我们可能在处理此函数或其兄弟的某个外部
	 * 执行中的失效消息时到达这里。锁的“清除”状态
	 * 告诉我们是否确实完成了吸收相关的失效消息。
	 */
	if (fc_res != LOCKACQUIRE_ALREADY_CLEAR)
	{
		AcceptInvalidationMessages();
		MarkLockClear(fc_locallock);
	}
}

/*
 *		ConditionalLockRelationOid
 *
 * 与上面相同，但仅在我们可以不阻塞地获得锁时才锁定。
 * 仅在获得锁时返回true。
 *
 * 注意：目前我们并不需要此文件中所有LockXXX例程的条件版本，
 * 但如果需要可以很容易添加。
 */
bool ConditionalLockRelationOid(Oid fc_relid, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;
	LOCALLOCK  *fc_locallock;
	LockAcquireResult fc_res;

	fc_SetLocktagRelationOid(&fc_tag, fc_relid);

	fc_res = LockAcquireExtended(&fc_tag, fc_lockmode, false, true, true, &fc_locallock);

	if (fc_res == LOCKACQUIRE_NOT_AVAIL)
		return false;

	/*
	 * 现在我们已经获得了锁，检查无效消息；请参见 LockRelationOid 中的说明。
	 */
	if (fc_res != LOCKACQUIRE_ALREADY_CLEAR)
	{
		AcceptInvalidationMessages();
		MarkLockClear(fc_locallock);
	}

	return true;
}

/*
 * LockRelationId
 * 
 * 锁定，给定一个 LockRelId。这与 LockRelationOid 相同，但以 LockRelId 作为输入。
 */
void LockRelationId(LockRelId *fc_relid, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;
	LOCALLOCK  *fc_locallock;
	LockAcquireResult fc_res;

	SET_LOCKTAG_RELATION(fc_tag, fc_relid->dbId, fc_relid->relId);

	fc_res = LockAcquireExtended(&fc_tag, fc_lockmode, false, false, true, &fc_locallock);

	/*
	 * 现在我们已经获得了锁，检查无效消息；请参见 LockRelationOid 中的说明。
	 */
	if (fc_res != LOCKACQUIRE_ALREADY_CLEAR)
	{
		AcceptInvalidationMessages();
		MarkLockClear(fc_locallock);
	}
}

/*
 * UnlockRelationId
 * 
 * 解锁，给定一个 LockRelId。出于速度原因，优先选择此方法而不是 UnlockRelationOid。
 */
void UnlockRelationId(LockRelId *fc_relid, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_RELATION(fc_tag, fc_relid->dbId, fc_relid->relId);

	LockRelease(&fc_tag, fc_lockmode, false);
}

/*
 * UnlockRelationOid
 * 
 * 解锁，仅给定一个关系 Oid。如果可以，请使用 UnlockRelationId。
 */
void UnlockRelationOid(Oid fc_relid, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	fc_SetLocktagRelationOid(&fc_tag, fc_relid);

	LockRelease(&fc_tag, fc_lockmode, false);
}

/*
 * LockRelation
 * 
 * 这是一个用于在已打开关系上获取额外锁的便利例程。永远不要尝试执行 "relation_open(foo, NoLock)"，然后用此方法锁定。
 */
void LockRelation(Relation fc_relation, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;
	LOCALLOCK  *fc_locallock;
	LockAcquireResult fc_res;

	SET_LOCKTAG_RELATION(fc_tag,
						 fc_relation->rd_lockInfo.lockRelId.dbId,
						 fc_relation->rd_lockInfo.lockRelId.relId);

	fc_res = LockAcquireExtended(&fc_tag, fc_lockmode, false, false, true, &fc_locallock);

	/*
	 * 现在我们已经获得了锁，检查无效消息；请参见 LockRelationOid 中的说明。
	 */
	if (fc_res != LOCKACQUIRE_ALREADY_CLEAR)
	{
		AcceptInvalidationMessages();
		MarkLockClear(fc_locallock);
	}
}

/*
 * ConditionalLockRelation
 * 
 * 这是一个用于在已打开关系上获取额外锁的便利例程。永远不要尝试执行 "relation_open(foo, NoLock)"，然后用此方法锁定。
 */
bool ConditionalLockRelation(Relation fc_relation, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;
	LOCALLOCK  *fc_locallock;
	LockAcquireResult fc_res;

	SET_LOCKTAG_RELATION(fc_tag,
						 fc_relation->rd_lockInfo.lockRelId.dbId,
						 fc_relation->rd_lockInfo.lockRelId.relId);

	fc_res = LockAcquireExtended(&fc_tag, fc_lockmode, false, true, true, &fc_locallock);

	if (fc_res == LOCKACQUIRE_NOT_AVAIL)
		return false;

	/*
	 * 现在我们已经获得了锁，检查无效消息；请参见 LockRelationOid 中的说明。
	 */
	if (fc_res != LOCKACQUIRE_ALREADY_CLEAR)
	{
		AcceptInvalidationMessages();
		MarkLockClear(fc_locallock);
	}

	return true;
}

/*
 * UnlockRelation
 * 
 * 这是一个用于解锁关系而不关闭它的便利例程。
 */
void UnlockRelation(Relation fc_relation, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_RELATION(fc_tag,
						 fc_relation->rd_lockInfo.lockRelId.dbId,
						 fc_relation->rd_lockInfo.lockRelId.relId);

	LockRelease(&fc_tag, fc_lockmode, false);
}

/*
 * CheckRelationLockedByMe
 * 
 * 如果当前事务持有 'relation' 的 'lockmode' 锁，则返回 true。如果 'orstronger' 为 true，则更强的锁模式也可以。
 * （“更强”定义为“数值更高”，这在语义上有点可疑，但对于我们的用途是可以的。）
 */
bool CheckRelationLockedByMe(Relation fc_relation, LOCKMODE fc_lockmode, bool fc_orstronger)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_RELATION(fc_tag,
						 fc_relation->rd_lockInfo.lockRelId.dbId,
						 fc_relation->rd_lockInfo.lockRelId.relId);

	return (fc_orstronger ?
			LockOrStrongerHeldByMe(&fc_tag, fc_lockmode) :
			LockHeldByMe(&fc_tag, fc_lockmode));
}

/*
 * CheckRelationOidLockedByMe
 * 
 * 类似于上面，但接受 OID 作为参数。
 */
bool CheckRelationOidLockedByMe(Oid fc_relid, LOCKMODE fc_lockmode, bool fc_orstronger)
{
	LOCKTAG		fc_tag;

	fc_SetLocktagRelationOid(&fc_tag, fc_relid);

	return (fc_orstronger ?
			LockOrStrongerHeldByMe(&fc_tag, fc_lockmode) :
			LockHeldByMe(&fc_tag, fc_lockmode));
}

/*
 * LockHasWaitersRelation
 * 
 * 这是一个检查其他人是否在等待我们当前持有的锁的函数。
 */
bool LockHasWaitersRelation(Relation fc_relation, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_RELATION(fc_tag,
						 fc_relation->rd_lockInfo.lockRelId.dbId,
						 fc_relation->rd_lockInfo.lockRelId.relId);

	return LockHasWaiters(&fc_tag, fc_lockmode, false);
}

/*
 * LockRelationIdForSession
 * 
 * 这个例程在目标关系上获取一个会话级别的锁。会话锁在事务边界之间保持持久。当调用 UnlockRelationIdForSession() 时，它将被移除，或者如果发生 ereport(ERROR)，或者如果后端退出。
 * 
 * 请注意，在任何实际使用关系的事务中，还应在关系上获得一个事务级别的锁，以确保关系缓存条目是最新的。
 */
void LockRelationIdForSession(LockRelId *fc_relid, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_RELATION(fc_tag, fc_relid->dbId, fc_relid->relId);

	(void) LockAcquire(&fc_tag, fc_lockmode, true, false);
}

/*
 * UnlockRelationIdForSession
 */
void UnlockRelationIdForSession(LockRelId *fc_relid, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_RELATION(fc_tag, fc_relid->dbId, fc_relid->relId);

	LockRelease(&fc_tag, fc_lockmode, true);
}

/*
 * LockRelationForExtension
 * 
 * 此锁标签用于互锁添加页面至关系。我们需要这样的锁定，因为 bufmgr/smgr 对 P_NEW 的定义并不是防止竞争条件的。
 * 
 * 我们假设调用者已经在关系上持有某种类型的常规锁，因此这里不需要 AcceptInvalidationMessages 调用。
 */
void LockRelationForExtension(Relation fc_relation, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_RELATION_EXTEND(fc_tag,
								fc_relation->rd_lockInfo.lockRelId.dbId,
								fc_relation->rd_lockInfo.lockRelId.relId);

	(void) LockAcquire(&fc_tag, fc_lockmode, false, false);
}

/*
 * ConditionalLockRelationForExtension
 * 
 * 与上面相同，但仅在可以不阻塞地获取锁时才锁定。仅在成功获取锁时返回 true。
 */
bool ConditionalLockRelationForExtension(Relation fc_relation, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_RELATION_EXTEND(fc_tag,
								fc_relation->rd_lockInfo.lockRelId.dbId,
								fc_relation->rd_lockInfo.lockRelId.relId);

	return (LockAcquire(&fc_tag, fc_lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL);
}

/*
 * RelationExtensionLockWaiterCount
 * 
 * 计算等待给定关系扩展锁的进程数量。
 */
int RelationExtensionLockWaiterCount(Relation fc_relation)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_RELATION_EXTEND(fc_tag,
								fc_relation->rd_lockInfo.lockRelId.dbId,
								fc_relation->rd_lockInfo.lockRelId.relId);

	return LockWaiterCount(&fc_tag);
}

/*
 * UnlockRelationForExtension
 */
void UnlockRelationForExtension(Relation fc_relation, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_RELATION_EXTEND(fc_tag,
								fc_relation->rd_lockInfo.lockRelId.dbId,
								fc_relation->rd_lockInfo.lockRelId.relId);

	LockRelease(&fc_tag, fc_lockmode, false);
}

/*
 * LockDatabaseFrozenIds
 * 
 * 这允许每个数据库的一个后端执行 vac_update_datfrozenxid()。
 */
void LockDatabaseFrozenIds(LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_DATABASE_FROZEN_IDS(fc_tag, MyDatabaseId);

	(void) LockAcquire(&fc_tag, fc_lockmode, false, false);
}

/*
 * LockPage
 * 
 * 获取页面级别的锁。这当前被一些索引访问方法用于锁定单个索引页面。
 */
void LockPage(Relation fc_relation, BlockNumber fc_blkno, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_PAGE(fc_tag,
					 fc_relation->rd_lockInfo.lockRelId.dbId,
					 fc_relation->rd_lockInfo.lockRelId.relId,
					 fc_blkno);

	(void) LockAcquire(&fc_tag, fc_lockmode, false, false);
}

/*
 * ConditionalLockPage
 * 
 * 与上面相同，但仅在可以不阻塞地获取锁时才锁定。仅在成功获取锁时返回 true。
 */
bool ConditionalLockPage(Relation fc_relation, BlockNumber fc_blkno, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_PAGE(fc_tag,
					 fc_relation->rd_lockInfo.lockRelId.dbId,
					 fc_relation->rd_lockInfo.lockRelId.relId,
					 fc_blkno);

	return (LockAcquire(&fc_tag, fc_lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL);
}

/*
 * UnlockPage
 */
void UnlockPage(Relation fc_relation, BlockNumber fc_blkno, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_PAGE(fc_tag,
					 fc_relation->rd_lockInfo.lockRelId.dbId,
					 fc_relation->rd_lockInfo.lockRelId.relId,
					 fc_blkno);

	LockRelease(&fc_tag, fc_lockmode, false);
}

/*
 * LockTuple
 * 
 * 获取元组级别的锁。这以一种不太直观的方式使用，因为我们无法在共享内存中为每个元组保持单独的锁。在使用之前，请参见 heap_lock_tuple！
 */
void LockTuple(Relation fc_relation, ItemPointer fc_tid, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_TUPLE(fc_tag,
					  fc_relation->rd_lockInfo.lockRelId.dbId,
					  fc_relation->rd_lockInfo.lockRelId.relId,
					  ItemPointerGetBlockNumber(fc_tid),
					  ItemPointerGetOffsetNumber(fc_tid));

	(void) LockAcquire(&fc_tag, fc_lockmode, false, false);
}

/*
 * ConditionalLockTuple
 * 
 * 与上面相同，但仅在可以不阻塞地获取锁时才锁定。仅在成功获取锁时返回 true。
 */
bool ConditionalLockTuple(Relation fc_relation, ItemPointer fc_tid, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_TUPLE(fc_tag,
					  fc_relation->rd_lockInfo.lockRelId.dbId,
					  fc_relation->rd_lockInfo.lockRelId.relId,
					  ItemPointerGetBlockNumber(fc_tid),
					  ItemPointerGetOffsetNumber(fc_tid));

	return (LockAcquire(&fc_tag, fc_lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL);
}

/*
 * UnlockTuple
 */
void UnlockTuple(Relation fc_relation, ItemPointer fc_tid, LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_TUPLE(fc_tag,
					  fc_relation->rd_lockInfo.lockRelId.dbId,
					  fc_relation->rd_lockInfo.lockRelId.relId,
					  ItemPointerGetBlockNumber(fc_tid),
					  ItemPointerGetOffsetNumber(fc_tid));

	LockRelease(&fc_tag, fc_lockmode, false);
}

/*
 * XactLockTableInsert
 * 
 * 插入一个锁，显示给定的事务 ID 正在运行——这是在事务或子事务获取 XID 时进行的。该锁可以用于等待事务完成。
 */
void XactLockTableInsert(TransactionId fc_xid)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_TRANSACTION(fc_tag, fc_xid);

	(void) LockAcquire(&fc_tag, ExclusiveLock, false, false);
}

/*
 * XactLockTableDelete
 * 
 * 删除显示给定事务 ID 正在运行的锁。（这从未用于主要事务 ID；这些锁仅在事务结束时隐式释放。但是我们确实使用它来处理子事务 ID。）
 */
void XactLockTableDelete(TransactionId fc_xid)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_TRANSACTION(fc_tag, fc_xid);

	LockRelease(&fc_tag, ExclusiveLock, false);
}

/*
 * XactLockTableWait
 * 
 * 等待指定的事务提交或中止。如果指定了操作，则设置错误上下文回调。如果将 'oper' 传递为 None，则不设置错误上下文回调。
 * 
 * 请注意，这对于子事务做得正确：如果我们等待子事务，一旦它中止或其顶部父事务提交，我们将立即退出。确保这一点需要一些额外的工作，因为为了节省共享内存，子事务的 XID 锁在结束时释放，不论成功与否。因此，我们必须检查它是否“仍在运行”，如果是，则等待其父事务。
 */
void XactLockTableWait(TransactionId fc_xid, Relation fc_rel, ItemPointer fc_ctid,
				  XLTW_Oper fc_oper)
{
	LOCKTAG		fc_tag;
	XactLockTableWaitInfo fc_info;
	ErrorContextCallback fc_callback;
	bool		fc_first = true;

	/*
	 * 如果指定了操作，请设置详细错误上下文回调。
	 */
	if (fc_oper != XLTW_None)
	{
		Assert(RelationIsValid(fc_rel));
		Assert(ItemPointerIsValid(fc_ctid));

		fc_info.rel = fc_rel;
		fc_info.ctid = fc_ctid;
		fc_info.oper = fc_oper;

		fc_callback.callback = fc_XactLockTableWaitErrorCb;
		fc_callback.arg = &fc_info;
		fc_callback.previous = error_context_stack;
		error_context_stack = &fc_callback;
	}

	for (;;)
	{
		Assert(TransactionIdIsValid(fc_xid));
		Assert(!TransactionIdEquals(fc_xid, GetTopTransactionIdIfAny()));

		SET_LOCKTAG_TRANSACTION(fc_tag, fc_xid);

		(void) LockAcquire(&fc_tag, ShareLock, false, false);

		LockRelease(&fc_tag, ShareLock, false);

		if (!TransactionIdIsInProgress(fc_xid))
			break;

		/*
		 * 如果 Xid 属于一个子事务，那么锁会在它完成后立即消失；为了正确的元组可见性，正确的做法是等待它的父事务消失。 
		 * 但是，我们可以直接等待最上层的事务完成，以达到相同的结果，这样也会减少锁表流量。
		 *
		 * 该函数的某些用途不涉及元组可见性——例如在为逻辑解码构建快照时。可以在事务在锁表中注册之前在 ProcArray 中看到它。 
		 * 在这种情况下，最上层的事务是相同的 xid，因此我们在短暂休眠后再试一次。 
		 * （第一次执行时不要休眠，以避免减慢正常情况。）
		 */
		if (!fc_first)
			pg_usleep(1000L);
		fc_first = false;
		fc_xid = SubTransGetTopmostTransaction(fc_xid);
	}

	if (fc_oper != XLTW_None)
		error_context_stack = fc_callback.previous;
}

/*
 *		ConditionalXactLockTableWait
 *
 * 与上述相同，但仅在不阻塞的情况下获取锁。 
 * 如果锁被成功获取，则返回 true。
 */
bool ConditionalXactLockTableWait(TransactionId fc_xid)
{
	LOCKTAG		fc_tag;
	bool		fc_first = true;

	for (;;)
	{
		Assert(TransactionIdIsValid(fc_xid));
		Assert(!TransactionIdEquals(fc_xid, GetTopTransactionIdIfAny()));

		SET_LOCKTAG_TRANSACTION(fc_tag, fc_xid);

		if (LockAcquire(&fc_tag, ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL)
			return false;

		LockRelease(&fc_tag, ShareLock, false);

		if (!TransactionIdIsInProgress(fc_xid))
			break;

		/* 有关此情况，请参见 XactLockTableWait */
		if (!fc_first)
			pg_usleep(1000L);
		fc_first = false;
		fc_xid = SubTransGetTopmostTransaction(fc_xid);
	}

	return true;
}

/*
 *		SpeculativeInsertionLockAcquire
 *
 * 插入一个锁，表明给定事务 ID 正在插入一个元组，但尚未决定是否要保留它。 
 * 然后可以使用该锁等待决定是否继续插入或终止。
 *
 * 该令牌用于区分同一事务的多个插入。它将返回给调用者。
 */
uint32 SpeculativeInsertionLockAcquire(TransactionId fc_xid)
{
	LOCKTAG		fc_tag;

	speculativeInsertionToken++;

	/*
	 * 检查是否存在环绕。零表示没有令牌被持有，因此不要使用它。
	 */
	if (speculativeInsertionToken == 0)
		speculativeInsertionToken = 1;

	SET_LOCKTAG_SPECULATIVE_INSERTION(fc_tag, fc_xid, speculativeInsertionToken);

	(void) LockAcquire(&fc_tag, ExclusiveLock, false, false);

	return speculativeInsertionToken;
}

/*
 *		投机性插入锁释放
 *
 * 删除显示给定事务正在投机性插入元组的锁。
 */
void SpeculativeInsertionLockRelease(TransactionId fc_xid)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_SPECULATIVE_INSERTION(fc_tag, fc_xid, speculativeInsertionToken);

	LockRelease(&fc_tag, ExclusiveLock, false);
}

/*
 *		投机性插入等待
 *
 * 等待指定事务完成或中止插入元组。
 */
void SpeculativeInsertionWait(TransactionId fc_xid, uint32 fc_token)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_SPECULATIVE_INSERTION(fc_tag, fc_xid, fc_token);

	Assert(TransactionIdIsValid(fc_xid));
	Assert(fc_token != 0);

	(void) LockAcquire(&fc_tag, ShareLock, false, false);
	LockRelease(&fc_tag, ShareLock, false);
}

/*
 * XactLockTableWaitErrorCb
 *		事务锁等待的错误上下文回调。
 */
static void fc_XactLockTableWaitErrorCb(void *fc_arg)
{
	XactLockTableWaitInfo *fc_info = (XactLockTableWaitInfo *) fc_arg;

	/*
	 * 我们也想打印架构名称，但这需要进行系统缓存查找。
	 */
	if (fc_info->oper != XLTW_None &&
		ItemPointerIsValid(fc_info->ctid) && RelationIsValid(fc_info->rel))
	{
		const char *fc_cxt;

		switch (fc_info->oper)
		{
			case XLTW_Update:
				fc_cxt = gettext_noop("while updating tuple (%u,%u) in relation \"%s\"");
				break;
			case XLTW_Delete:
				fc_cxt = gettext_noop("while deleting tuple (%u,%u) in relation \"%s\"");
				break;
			case XLTW_Lock:
				fc_cxt = gettext_noop("while locking tuple (%u,%u) in relation \"%s\"");
				break;
			case XLTW_LockUpdated:
				fc_cxt = gettext_noop("while locking updated version (%u,%u) of tuple in relation \"%s\"");
				break;
			case XLTW_InsertIndex:
				fc_cxt = gettext_noop("while inserting index tuple (%u,%u) in relation \"%s\"");
				break;
			case XLTW_InsertIndexUnique:
				fc_cxt = gettext_noop("while checking uniqueness of tuple (%u,%u) in relation \"%s\"");
				break;
			case XLTW_FetchUpdated:
				fc_cxt = gettext_noop("while rechecking updated tuple (%u,%u) in relation \"%s\"");
				break;
			case XLTW_RecheckExclusionConstr:
				fc_cxt = gettext_noop("while checking exclusion constraint on tuple (%u,%u) in relation \"%s\"");
				break;

			default:
				return;
		}

		errcontext(fc_cxt,
				   ItemPointerGetBlockNumber(fc_info->ctid),
				   ItemPointerGetOffsetNumber(fc_info->ctid),
				   RelationGetRelationName(fc_info->rel));
	}
}

/*
 * 等待多个锁持有者
 *		等待直到没有事务持有与给定锁标签在给定锁模式下冲突的锁。
 *
 * 为此，获取当前锁持有者的列表，并根据他们的VXIDs等待，直到他们完成。
 *
 * 注意我们不尝试获取给定锁标签上的锁，仅获取其锁持有者的VXIDs和XIDs；如果在我们获得初始锁持有者列表后，有人获取了冲突的对象锁，我们将不会等待他们。
 */
void WaitForLockersMultiple(List *fc_locktags, LOCKMODE fc_lockmode, bool fc_progress)
{
	List	   *fc_holders = NIL;
	ListCell   *fc_lc;
	int			fc_total = 0;
	int			fc_done = 0;

	/* 如果没有锁需要等待则完成 */
	if (list_length(fc_locktags) == 0)
		return;

	/* 收集我们需要等待的事务 */
	foreach(fc_lc, fc_locktags)
	{
		LOCKTAG    *fc_locktag = lfirst(fc_lc);
		int			fc_count;

		fc_holders = lappend(fc_holders,
						  GetLockConflicts(fc_locktag, fc_lockmode,
										   fc_progress ? &fc_count : NULL));
		if (fc_progress)
			fc_total += fc_count;
	}

	if (fc_progress)
		pgstat_progress_update_param(PROGRESS_WAITFOR_TOTAL, fc_total);

	/*
	 * 注意：GetLockConflicts()永远不会报告我们自己的xid，因此我们不需要检查这一点。此外，准备好的事务会被报告和等待。
	 */

	/* 最后等待每个这样的事务完成 */
	foreach(fc_lc, fc_holders)
	{
		VirtualTransactionId *fc_lockholders = lfirst(fc_lc);

		while (VirtualTransactionIdIsValid(*fc_lockholders))
		{
			/* 如果请求，发布我们要等待谁。 */
			if (fc_progress)
			{
				PGPROC	   *fc_holder = BackendIdGetProc(fc_lockholders->backendId);

				if (fc_holder)
					pgstat_progress_update_param(PROGRESS_WAITFOR_CURRENT_PID,
												 fc_holder->pid);
			}
			VirtualXactLock(*fc_lockholders, true);
			fc_lockholders++;

			if (fc_progress)
				pgstat_progress_update_param(PROGRESS_WAITFOR_DONE, ++fc_done);
		}
	}
	if (fc_progress)
	{
		const int	fc_index[] = {
			PROGRESS_WAITFOR_TOTAL,
			PROGRESS_WAITFOR_DONE,
			PROGRESS_WAITFOR_CURRENT_PID
		};
		const int64 fc_values[] = {
			0, 0, 0
		};

		pgstat_progress_update_multi_param(3, fc_index, fc_values);
	}

	list_free_deep(fc_holders);
}

/*
 * 等待锁持有者
 *
 * 与等待多个锁持有者相同，用于单个锁标签。
 */
void WaitForLockers(LOCKTAG fc_heaplocktag, LOCKMODE fc_lockmode, bool fc_progress)
{
	List	   *fc_l;

	fc_l = list_make1(&fc_heaplocktag);
	WaitForLockersMultiple(fc_l, fc_lockmode, fc_progress);
	list_free(fc_l);
}


/*
 *		锁定数据库对象
 *
 * 获取当前数据库的通用对象的锁。不要用于共享对象（如表空间）。将其应用于关系也是不明智的，因为以这种方式获取的锁不会与通过LockRelation及其朋友获得的锁冲突。
 */
void LockDatabaseObject(Oid fc_classid, Oid fc_objid, uint16 fc_objsubid,
				   LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_OBJECT(fc_tag,
					   MyDatabaseId,
					   fc_classid,
					   fc_objid,
					   fc_objsubid);

	(void) LockAcquire(&fc_tag, fc_lockmode, false, false);

	/* 确保系统缓存与我们等待的任何更改保持最新 */
	AcceptInvalidationMessages();
}

/*
 *		条件锁定数据库对象
 *
 * 如上，但仅在我们可以在不阻塞的情况下获取锁时锁定。
 * 仅在获取锁时返回true。
 */
bool ConditionalLockDatabaseObject(Oid fc_classid, Oid fc_objid, uint16 fc_objsubid,
							  LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;
	LOCALLOCK  *fc_locallock;
	LockAcquireResult fc_res;

	SET_LOCKTAG_OBJECT(fc_tag,
					   MyDatabaseId,
					   fc_classid,
					   fc_objid,
					   fc_objsubid);

	fc_res = LockAcquireExtended(&fc_tag, fc_lockmode, false, true, true, &fc_locallock);

	if (fc_res == LOCKACQUIRE_NOT_AVAIL)
		return false;

	/*
	 * 现在我们已经获得了锁，检查无效消息；请参见 LockRelationOid 中的说明。
	 */
	if (fc_res != LOCKACQUIRE_ALREADY_CLEAR)
	{
		AcceptInvalidationMessages();
		MarkLockClear(fc_locallock);
	}

	return true;
}

/*
 *		解锁数据库对象
 */
void UnlockDatabaseObject(Oid fc_classid, Oid fc_objid, uint16 fc_objsubid,
					 LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_OBJECT(fc_tag,
					   MyDatabaseId,
					   fc_classid,
					   fc_objid,
					   fc_objsubid);

	LockRelease(&fc_tag, fc_lockmode, false);
}

/*
 *		锁定共享对象
 *
 * 获取一个跨数据库共享对象的锁。
 */
void LockSharedObject(Oid fc_classid, Oid fc_objid, uint16 fc_objsubid,
				 LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_OBJECT(fc_tag,
					   InvalidOid,
					   fc_classid,
					   fc_objid,
					   fc_objsubid);

	(void) LockAcquire(&fc_tag, fc_lockmode, false, false);

	/* 确保系统缓存与我们等待的任何更改保持最新 */
	AcceptInvalidationMessages();
}

/*
 *		解锁共享对象
 */
void UnlockSharedObject(Oid fc_classid, Oid fc_objid, uint16 fc_objsubid,
				   LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_OBJECT(fc_tag,
					   InvalidOid,
					   fc_classid,
					   fc_objid,
					   fc_objsubid);

	LockRelease(&fc_tag, fc_lockmode, false);
}

/*
 *		为会话锁定共享对象
 *
 * 获取跨数据库共享对象的会话级锁。
 * 有关会话级锁的说明，请参见LockRelationIdForSession。
 */
void LockSharedObjectForSession(Oid fc_classid, Oid fc_objid, uint16 fc_objsubid,
						   LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_OBJECT(fc_tag,
					   InvalidOid,
					   fc_classid,
					   fc_objid,
					   fc_objsubid);

	(void) LockAcquire(&fc_tag, fc_lockmode, true, false);
}

/*
 *		为会话解锁共享对象
 */
void UnlockSharedObjectForSession(Oid fc_classid, Oid fc_objid, uint16 fc_objsubid,
							 LOCKMODE fc_lockmode)
{
	LOCKTAG		fc_tag;

	SET_LOCKTAG_OBJECT(fc_tag,
					   InvalidOid,
					   fc_classid,
					   fc_objid,
					   fc_objsubid);

	LockRelease(&fc_tag, fc_lockmode, true);
}


/*
 * 将锁定对象的描述附加到buf。
 *
 * 理想情况下，我们会为数字值打印名称，但这需要在系统表上获取锁，这可能会导致问题，因为这通常用于报告死锁情况。
 */
void DescribeLockTag(StringInfo fc_buf, const LOCKTAG *fc_tag)
{
	switch ((LockTagType) fc_tag->locktag_type)
	{
		case LOCKTAG_RELATION:
			appendStringInfo(fc_buf,
							 _("relation %u of database %u"),
							 fc_tag->locktag_field2,
							 fc_tag->locktag_field1);
			break;
		case LOCKTAG_RELATION_EXTEND:
			appendStringInfo(fc_buf,
							 _("extension of relation %u of database %u"),
							 fc_tag->locktag_field2,
							 fc_tag->locktag_field1);
			break;
		case LOCKTAG_DATABASE_FROZEN_IDS:
			appendStringInfo(fc_buf,
							 _("pg_database.datfrozenxid of database %u"),
							 fc_tag->locktag_field1);
			break;
		case LOCKTAG_PAGE:
			appendStringInfo(fc_buf,
							 _("page %u of relation %u of database %u"),
							 fc_tag->locktag_field3,
							 fc_tag->locktag_field2,
							 fc_tag->locktag_field1);
			break;
		case LOCKTAG_TUPLE:
			appendStringInfo(fc_buf,
							 _("tuple (%u,%u) of relation %u of database %u"),
							 fc_tag->locktag_field3,
							 fc_tag->locktag_field4,
							 fc_tag->locktag_field2,
							 fc_tag->locktag_field1);
			break;
		case LOCKTAG_TRANSACTION:
			appendStringInfo(fc_buf,
							 _("transaction %u"),
							 fc_tag->locktag_field1);
			break;
		case LOCKTAG_VIRTUALTRANSACTION:
			appendStringInfo(fc_buf,
							 _("virtual transaction %d/%u"),
							 fc_tag->locktag_field1,
							 fc_tag->locktag_field2);
			break;
		case LOCKTAG_SPECULATIVE_TOKEN:
			appendStringInfo(fc_buf,
							 _("speculative token %u of transaction %u"),
							 fc_tag->locktag_field2,
							 fc_tag->locktag_field1);
			break;
		case LOCKTAG_OBJECT:
			appendStringInfo(fc_buf,
							 _("object %u of class %u of database %u"),
							 fc_tag->locktag_field3,
							 fc_tag->locktag_field2,
							 fc_tag->locktag_field1);
			break;
		case LOCKTAG_USERLOCK:
			/* 保留给旧的贡献代码，现在在pgfoundry上 */
			appendStringInfo(fc_buf,
							 _("user lock [%u,%u,%u]"),
							 fc_tag->locktag_field1,
							 fc_tag->locktag_field2,
							 fc_tag->locktag_field3);
			break;
		case LOCKTAG_ADVISORY:
			appendStringInfo(fc_buf,
							 _("advisory lock [%u,%u,%u,%u]"),
							 fc_tag->locktag_field1,
							 fc_tag->locktag_field2,
							 fc_tag->locktag_field3,
							 fc_tag->locktag_field4);
			break;
		default:
			appendStringInfo(fc_buf,
							 _("unrecognized locktag type %d"),
							 (int) fc_tag->locktag_type);
			break;
	}
}

/*
 * 从标签类型获取锁名称
 *
 *	给定锁标签类型，返回相应的锁名称。
 */
const char * GetLockNameFromTagType(uint16 fc_locktag_type)
{
	if (fc_locktag_type > LOCKTAG_LAST_TYPE)
		return "???";
	return LockTagTypeNames[fc_locktag_type];
}
